
#include "service.h"

pthread_mutex_t mutex_wait_terminal;
pthread_mutex_t mutex_epoll_ctl;
vector<pair<wait_arg, wait_arg>> wait_terminal;

int epoll_fd;

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        printf("Usage: %s <ip> <port>\n", argv[0]);
        return -1;
    }
    int port = atoi(argv[2]);
    int listenfd = getListenfd(argv[1], port);
    printf("port %d\n", port);
    printf("ip %s\n", argv[1]);
    if (listenfd == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("getListenfd failed");
        return -1;
    }

    printf("监听套接字创建成功 %d\n", listenfd);
    puts("wait connect");

    pthread_mutex_init(&mutex_wait_terminal, NULL);
    pthread_mutex_init(&mutex_epoll_ctl, NULL);

    struct epoll_event event;
    epoll_fd = epoll_create(20);
    if (epoll_fd == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("epoll_create failed");
        exit(-1);
    }
    printf("epoll_fd is:%d\n", epoll_fd);
    event.events = EPOLLIN;
    event.data.fd = listenfd;
    
    // 添加
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &event) == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("epoll_ctl:listenfd\n");
        exit(-1);
    }
    event.events = EPOLLIN;
    event.data.fd = 0;

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event) == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("epoll_ctl:listenfd\n");
        exit(-1);
    }

    struct epoll_event *arr_event = (struct epoll_event *)malloc(sizeof(struct epoll_event) * 20);
    while (1)
    {
        int answer_len = epoll_wait(epoll_fd, arr_event, 20, -1);
        if (answer_len == -1)
        {
            fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
            perror("epoll_wait failed");
            exit(-1);
        }
        printf("answer_len is:%d\n", answer_len);

        for (int i = 0; i < answer_len; i++)
        {
            if (arr_event[i].data.fd == 0)
            {
                // 服务器控制信息

                continue;
            }
            else if (arr_event[i].data.fd == listenfd)
            {
                // 监听套接字接收连接请求，调用accept创建新套接字，并添加到epoll中进行监听。
                int com_fd = accept_comfd(listenfd);
                if (com_fd == -1)
                {
                    perror("accept failed");
                    continue;
                }
                const char chOpt=1;
                setsockopt(com_fd,IPPROTO_TCP,TCP_NODELAY,&chOpt,sizeof(char));
                printf("app or car通信套接字连接成功 %d\n", com_fd);
                epoll_add(com_fd, EPOLLIN);
            }
            else
            {
                // 未成功比配的小车/app 的数据处理
                // 接收并解析数据头
                struct head top;
                int rv = 0;
                printf("fd is:%d\n", arr_event[i].data.fd);
                rv = getHead(arr_event[i].data.fd, &top);
                if (rv == -1)
                {
                    perror("recv failed head");
                    printf("接收命令头失败，可能会导致后面数据失序。");
                }
                else if (rv == 0)
                {
                    // 将 连接移出等待队列,并且移出epoll监控。
                    printf("一个连接关闭:fd %d\n", arr_event[i].data.fd);
                    int index_ok = -1;
                    for (size_t index = 0; index < wait_terminal.size(); index++)
                    {
                        if (wait_terminal[index].first.fd == arr_event[i].data.fd || wait_terminal[index].second.fd == arr_event[i].data.fd)
                        {
                            index_ok = index;
                            break;
                        }
                    }
                    if (index_ok != -1)
                    {
                        pthread_mutex_lock(&mutex_wait_terminal);
                        wait_terminal.erase(wait_terminal.begin() + index_ok);
                        pthread_mutex_unlock(&mutex_wait_terminal);
                    }
                    epoll_remove(arr_event[i].data.fd);
                    continue;
                }

                printf("top:%d,%lld,%d\n", top.data_type, top.size, top.cmd_type);

                if (top.cmd_type == REQUST)
                {
                    // 处理 app连接请求小车，或者小车请求连接app
                    int index = 0;
                    //请求的 小车/app是否存在
                    index = find_client_iswait(top.data_type * -1);

                    // 自身是否已添加
                    int flag = find_client_iswait(top.data_type);

                    if (top.data_type < 0)
                    {
                        puts("小车请求连接");
                        // 小车 请求连接 app
                        // 查找对应app 是否已在等待队列中。

                        if (index != -1)
                        {
                            // 将小车添加到 包含对应app的等待中。
                            wait_terminal[index].second.fd = arr_event[i].data.fd;
                            wait_terminal[index].second.top = top;
                        }
                        else if (flag == -1)
                        {
                            // 没有找到app,且 小车未在等待队列中
                            // 将小车添加到等待队列中.
                            pair<wait_arg, wait_arg> node;
                            node.first.top.data_type = 0;
                            node.second.fd = arr_event[i].data.fd;
                            node.second.top = top;
                            pthread_mutex_lock(&mutex_wait_terminal);
                            wait_terminal.push_back(node);
                            pthread_mutex_unlock(&mutex_wait_terminal);
                        }
                    }
                    else if (top.data_type > 0)
                    {
                        // app 请求连接 小车
                        puts("app请求连接");
                        if (index != -1)
                        {
                            // 将app添加到 包含对应小车的等待中。
                            wait_terminal[index].first.fd = arr_event[i].data.fd;
                            wait_terminal[index].first.top = top;
                        }
                        else if (flag == -1)
                        {
                            // 没有找到小车
                            // 将小车添加到等待队列中.
                            pair<wait_arg, wait_arg> node;
                            node.second.top.data_type = 0;
                            node.first.fd = arr_event[i].data.fd;
                            node.first.top = top;
                            pthread_mutex_lock(&mutex_wait_terminal);
                            wait_terminal.push_back(node);
                            pthread_mutex_unlock(&mutex_wait_terminal);
                        }
                    }

                    if (index != -1)
                    {
                        // app和小车成功匹配 开子线程供其通信，且将他们移除等待队列以及epoll监控。

                        printf("匹配成功%d,%d\n", wait_terminal[index].first.fd, wait_terminal[i].second.fd);
                        // 匹配成功
                        thread_args arg = {.app = wait_terminal[index].first, .car = wait_terminal[index].second, .arr_com = wait_terminal, .epoll_fd = epoll_fd};
                        pthread_t thread_id;
                        // app 和小车 就绪，创建线程管理
                        if (pthread_create(&thread_id, NULL, Com_thread, (void *)&arg) != 0)
                        {
                            perror("pthread_create fail");
                            // 关闭连接
                        }
                        else
                        {
                            pthread_detach(thread_id);

                            // 从epoll监控中移除
                            epoll_remove(wait_terminal[index].first.fd);
                            epoll_remove(wait_terminal[index].second.fd);

                            //移除等待队列
                            pthread_mutex_lock(&mutex_wait_terminal);
                            wait_terminal.erase(wait_terminal.begin() + index);
                            pthread_mutex_unlock(&mutex_wait_terminal);
                        }
                    }
                }
                else
                {
                    //  未成功比配的小车/app 的非请求数据处理
                    char *data = (char *)malloc(top.size * sizeof(char));
                    if (data == NULL)
                    {
                        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
                        perror("malloc failed");
                        return -1;
                    }
                    rv = recv(arr_event[i].data.fd, data, top.size, MSG_WAITALL);
                    if (rv == -1)
                    {
                        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
                        perror("recv failed");
                        fprintf(stderr,"接收命令数据失败，可能会导致后面数据失序。");
                        free(data);
                        return -1;
                    }
                    else if (rv == 0)
                    {
                        printf("close client\n");
                        //释放epoll 监管，以及wait等待
                        epoll_remove(arr_event[i].data.fd);

                        int index = find_client_iswait(top.data_type);
                        if (index != -1)
                        {
                            pthread_mutex_lock(&mutex_wait_terminal);
                            wait_terminal.erase(wait_terminal.begin() + index);
                            pthread_mutex_unlock(&mutex_wait_terminal);
                        }
                        free(data);
                    }
                    else
                    {
                        if (top.cmd_type == VIDEO)
                        {
                            // 保存图片
                            // static int video_index = 0;
                            // char buf_temp[32] = {0};
                            // sprintf(buf_temp, "./img%d.jpg", video_index++);

                            // int fd_img = open(buf_temp, O_CREAT | O_WRONLY, 0666);
                            // if (fd_img == -1)
                            // {
                            //     perror("open failed");
                            // }
                            // else
                            // {
                            //     int send_size = 0;
                            //     if ((send_size = write(fd_img, data, top.size)) != 1)
                            //     {
                            //         perror("write failed");
                            //     }
                            //     printf("send %s size is:%d done img\n", buf_temp, send_size);
                            //     close(fd_img);
                            // }
                            // puts(buf_temp);
                        }
                        else
                        {
                            FILE *fp = NULL; // fopen("data_temp.txt", "a+");
                            if (fp == NULL)
                            {
                                perror("not open data_temp.txt");
                                for (int i = 0; i < top.size; i++)
                                {
                                    printf("%02x", data[i]);
                                }
                                printf("\n");
                                printf("data: %s\n", data);
                                // puts(data);
                            }
                            else
                            {
                                fwrite(data, top.size, 1, fp);
                                fclose(fp);
                            }
                        }
                        // 未连接成功时 发送的数据
                        free(data);
                    }
                }
            }
        }
    }

    free(arr_event);

    pthread_mutex_destroy(&mutex_wait_terminal);
    pthread_mutex_destroy(&mutex_epoll_ctl);

    /* code */
    return 0;
}

int find_client_iswait(int client_data_type)
{
    if (client_data_type > 0)
    {
        for (size_t i = 0; i < wait_terminal.size(); i++)
            if (wait_terminal[i].first.top.data_type == client_data_type)
                return i;
    }
    else
    {
        for (size_t i = 0; i < wait_terminal.size(); i++)
            if (wait_terminal[i].second.top.data_type == client_data_type)
                return i;
    }
    // 没有找到
    return -1;
}

int accept_comfd(int listenfd)
{
    sockaddr_in client_addr;
    socklen_t addrlen = sizeof(struct sockaddr_in);
    int com_fd = accept(listenfd, (struct sockaddr *)&client_addr, &addrlen);
    return com_fd;
}

void epoll_add(int fd, uint32_t events)
{
    struct epoll_event event_temp;
    event_temp.events = events;
    event_temp.data.fd = fd;
    pthread_mutex_lock(&mutex_epoll_ctl);
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event_temp) == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("epoll_ctl failed: fd");
        fprintf(stderr,"fd is %d\n", fd);
        exit(-1);
    }
    pthread_mutex_unlock(&mutex_epoll_ctl);
}

int getHead(int fd, head *top)
{
    int rv = 0;
    char top_buf[COM_TOP_SIZE] = {0};

    rv = recv(fd, top_buf, COM_TOP_SIZE, MSG_WAITALL);
    printf("fd=%d \n", fd);
    if (rv == COM_TOP_SIZE)
    {
        // 调用 解析头函数
        unPackCmdHead(top, top_buf);
    }
    return rv;
}

void epoll_remove(int fd)
{
    pthread_mutex_lock(&mutex_epoll_ctl);
    if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1)
    {
        fprintf(stderr, "%s:%d ", __FILE__,  __LINE__);
        perror("epoll_ctl del failed\n");
        exit(-1);
    }
    pthread_mutex_unlock(&mutex_epoll_ctl);
}
